#include "HelloWorldScene.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    // 'scene' is an autorelease object
    auto scene = Scene::create();
    
    // 'layer' is an autorelease object
    auto layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    if ( !Layer::init() )
    {
        return false;
    }
    
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Point origin = Director::getInstance()->getVisibleOrigin();
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
	closeItem->setPosition(Point(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                                origin.y + closeItem->getContentSize().height/2));
    
    auto startLabel = LabelTTF::create("start","Arial",20);
    auto setSPItem = MenuItemLabel::create(startLabel, std::bind(&HelloWorld::startPointCallback, this, std::placeholders::_1));
    setSPItem->setPosition(origin.x + visibleSize.width - startLabel->getContentSize().width,
                           closeItem->getContentSize().height * 4 + startLabel->getContentSize().height/2);
    
    auto endLabel = LabelTTF::create("end","Arial",20);
    auto setEPItem = MenuItemLabel::create(endLabel, std::bind(&HelloWorld::endPointCallback, this, std::placeholders::_1));
    setEPItem->setPosition(origin.x + visibleSize.width - endLabel->getContentSize().width,
                           closeItem->getContentSize().height * 3 + endLabel->getContentSize().height/2);
    
    auto obstacleLabel = LabelTTF::create("obstacle","Arial",20);
    auto setOPItem = MenuItemLabel::create(obstacleLabel, std::bind(&HelloWorld::obstaclePointCallback, this, std::placeholders::_1));
    setOPItem->setPosition(origin.x + visibleSize.width - obstacleLabel->getContentSize().width,
                           closeItem->getContentSize().height * 2 + obstacleLabel->getContentSize().height/2);
    
    
    auto menu = Menu::create(closeItem, setSPItem, setEPItem, setOPItem, NULL);
    menu->setPosition(Point::ZERO);
    this->addChild(menu, 1);
    Sprite *spriteBG = Sprite::create("level_1.png");
    spriteBG->setPosition(Point(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
    this->addChild(spriteBG, -1);
    
    setTouchEnabled(true);
    setTouchMode(Touch::DispatchMode::ONE_BY_ONE);
    
    m_nStep = STEP_STARTPOINT;
    m_nStartIndex = -1;
    m_nEndIndex = -1;
    initMap();
    
    Sprite *hero = Sprite::create("monster_5.png");
    this->addChild(hero,0,TAG_HERO);
    
    return true;
}

void HelloWorld::startPointCallback(cocos2d::Object *pSender) {
    CCLOG("start press");
    m_nStep = STEP_STARTPOINT;
}

void HelloWorld::endPointCallback(cocos2d::Object *pSender) {
    m_nStep = STEP_ENDPOINT;
}

void HelloWorld::obstaclePointCallback(cocos2d::Object *pSender) {
    m_nStep = STEP_DEFAULT;
}

void HelloWorld::menuCloseCallback(Object* pSender)
{
//    Director::getInstance()->end();
//
//#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
//    exit(0);
//#endif
    findPath();
    Sprite* spriteHero = (Sprite*)this->getChildByTag(TAG_HERO);
    m_PathNum = m_vecPath.size();
    spriteHero->setPosition(getNextPoint());
    this->schedule(schedule_selector(HelloWorld::heroMove), 0.3, m_vecPath.size() - 2, 0.1);
    
}

Point HelloWorld::getNextPoint() {
    static int nextPointNum = 1;
    Point nextPoint;
    Point ptObstacleLT;
    Point ptObstacleRD;
    //if (nextPointNum < m_vecPath.size()) {
        getRectPointByIndex(m_vecPath[m_PathNum - nextPointNum]->nIndex, ptObstacleLT, ptObstacleRD);
        nextPoint = Point((ptObstacleLT.x + ptObstacleRD.x)/2,(ptObstacleLT.y + ptObstacleRD.y)/2);
    //}
    nextPointNum ++;
    return nextPoint;
}

void HelloWorld::heroMove(float dt) {
    Sprite* spriteHero = (Sprite*)this->getChildByTag(TAG_HERO);
    FiniteTimeAction *actionMove = MoveTo::create(0.3,getNextPoint());
    spriteHero->runAction(actionMove);
    //spriteHero->setPosition(getNextPoint());
}

void HelloWorld::initMap() {
    srand((unsigned int) time(NULL));
    //随机生成障碍 40%概率
    for (int i = 0; i < getRow() * getCol(); ++i) {
        int nRandFlag = GRID_FLAG_DEFAULT;//((int)(CCRANDOM_0_1() * 10)) % 4 == 0 ? GRID_FLAG_OBSTACLE: GRID_FLAG_DEFAULT;
        ST_GRID *pGrid = new ST_GRID;
        if ( !pGrid ) {
            return ;
        }
        pGrid->gf = (GRID_FLAG)nRandFlag;
        m_vecGrid.push_back(pGrid);
    }
}

void HelloWorld::findPath() {
    vector<NODE*> vecClose;
    vector<NODE*> vecOpen;
    if (m_nStartIndex == -1 || m_nEndIndex == -1) {
        return ;
    }
    m_vecPath.clear();
    
    NODE* pNode = new NODE;
    pNode->nIndex = m_nStartIndex;
    vecClose.push_back(pNode);
    
    int nStep = 0;
    while (true) {
        if (nStep++ >= 400) {
            break;
        }
        NODE* pNextNode = vecClose[vecClose.size()  - 1];
        if (!pNextNode) {
            break;
        }
        if (pNextNode->nIndex == m_nEndIndex) {
            break;
        }
        
        for (int i = 0; i < 8; ++i) {
            int nIndex = getIndexByDir(pNextNode->nIndex, i);
            if (-1 == nIndex) {
                continue;
            }
            if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE) {
                continue;
            }
            if (inTable(nIndex, vecClose) != NULL) {
                continue;
            }
            NODE* pNode = inTable(nIndex, vecOpen);
            if (pNode) {
                int nNewG = pNextNode->nG + getGByIndex(pNextNode->nIndex, pNode->nIndex);
                if (pNode->nG > nNewG) {
                    pNode->nG = nNewG;
                    pNode->pParent = pNextNode;
                }
                continue;
            }
            pNode = new NODE;
            pNode->nIndex = nIndex;
            pNode->nG = pNextNode->nG + getGByIndex(pNextNode->nIndex, pNode->nIndex);
            pNode->pParent = pNextNode;
            vecOpen.push_back(pNode);
        }
        int nMinF = 0xFFFFFF;
        pNextNode = NULL;
        int nNextNodeIndex = 0;
        for (int i = 0; i < (int)vecOpen.size(); ++i) {
            NODE* pNode = vecOpen[i];
            if (!pNode) {
                continue;
            }
            int nH = getHByIndex(pNode->nIndex);
            int nF = nH + pNode->nG;
            if (nF < nMinF) {
                nMinF = nF;
                pNextNode = pNode;
                nNextNodeIndex = i;
            }
        }
        if (nNextNodeIndex >= 0 && nNextNodeIndex < (int)vecOpen.size()) {
            vecClose.push_back(pNextNode);
            vecOpen.erase(vecOpen.begin() + nNextNodeIndex);
        }
    }
    
    pNode = vecClose[vecClose.size() - 1];
    while (pNode) {
        m_vecPath.push_back(pNode);
        pNode = pNode->pParent;
    }
}

int HelloWorld::getRow() {
    Size size = Director::getInstance()->getWinSize();
    return size.height / GRID_SIDELEN;
}

int HelloWorld::getCol() {
    Size size = Director::getInstance()->getWinSize();
    return size.width / GRID_SIDELEN;
}

int HelloWorld::getIndexByPoint(Point pt) {
    pt.x = pt.x > (int)pt.x ? pt.x + 1 : pt.x;
    pt.y = pt.y > (int)pt.y ? pt.y + 1 : pt.y;
    return (int)pt.y / GRID_SIDELEN * getCol() + (int)pt.x / GRID_SIDELEN;
}

void HelloWorld::draw() {
    Size size = Director::getInstance()->getWinSize();
    for (int i = 0; i < getRow(); ++i) {
        DrawPrimitives::drawLine(Point(0, i * GRID_SIDELEN), Point(size.width, i* GRID_SIDELEN));
    }
    for (int i = 0; i < getCol(); ++i) {
        DrawPrimitives::drawLine(Point(i * GRID_SIDELEN, 0), Point(i * GRID_SIDELEN, size.height));
    }
    
    for (int i = 0; i < (int)m_vecPath.size(); ++i) {
        Point ptObstacleLT;
        Point ptObstacleRD;
        getRectPointByIndex(m_vecPath[i]->nIndex, ptObstacleLT, ptObstacleRD);
        Color4F clrObstacle = {0, 1, 1, 1};
        DrawPrimitives::drawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);
    }
    
    Point ptStartLT;
    Point ptStartRD;
    getRectPointByIndex(m_nStartIndex, ptStartLT, ptStartRD);
    Color4F clrStart = {1, 0, 0, 1};
    DrawPrimitives::drawSolidRect(ptStartLT, ptStartRD, clrStart);
    
    Point ptendLT;
    Point ptendRD;
    getRectPointByIndex(m_nEndIndex, ptendLT, ptendRD);
    Color4F clrEnd = {0, 1, 0, 1};
    DrawPrimitives::drawSolidRect(ptendLT, ptendRD, clrEnd);
    
    for (int i = 0; i < (int)m_vecGrid.size(); ++i) {
        if (m_vecGrid[i]->gf == GRID_FLAG_OBSTACLE) {
            Point ptObstacleLT;
            Point ptObstacleRD;
            getRectPointByIndex(i, ptObstacleLT, ptObstacleRD);
            Color4F clrObstacle = {0, 0, 1, 1};
            DrawPrimitives::drawSolidRect(ptObstacleLT, ptObstacleRD, clrObstacle);
        }
    }
}

void HelloWorld::getRectPointByIndex(int nIndex, cocos2d::Point &ptLT, cocos2d::Point &ptRD) {
    ptLT.x = nIndex % getCol() * GRID_SIDELEN;
    ptLT.y = nIndex / getCol() * GRID_SIDELEN;
    ptRD.x = ptLT.x + GRID_SIDELEN;
    ptRD.y = ptLT.y + GRID_SIDELEN;
}

int HelloWorld::getIndexByDir(int nIndex, int nDir) {
    if (nIndex < 0 || nIndex >= (int)m_vecGrid.size()) {
        return -1;
    }
    
    int nRow = nIndex / getCol();
    int nCol = nIndex % getCol();
    
    switch (nDir) {
        case 0:
            nRow += 1;
            break;
        case 1:
            nRow += 1;
            nCol += 1;
            break;
        case 2:
            nCol += 1;
            break;
        case 3:
            nRow -= 1;
            nCol += 1;
            break;
        case 4:
            nRow -= 1;
            break;
        case 5:
            nRow -= 1;
            nCol -= 1;
            break;
        case 6:
            nCol -= 1;
            break;
        case 7:
            nRow += 1;
            nCol -= 1;
            break;
        default:
            break;
    }
    if (nRow < 0 || nRow >= getRow() || nCol < 0 || nCol >= getCol()) {
        return -1;
    }
    return nRow * getCol() + nCol;
}

int HelloWorld::getGByIndex(int nStartIndex, int nEndIndex) {
    int nStartRow = nStartIndex / getCol();
    int nStartCol = nStartIndex % getCol();
    int nEndRow = nEndIndex / getCol();
    int nEndCol = nEndIndex % getCol();
    
    if (nStartRow == nEndRow || nStartCol == nEndCol) {
        return 32;
    }
    return 45;
}

int HelloWorld::getHByIndex(int nIndex) {
    int nRow = nIndex / getCol();
    int nCol = nIndex % getCol();
    int nEndRow = m_nEndIndex / getCol();
    int nEndCol = m_nEndIndex % getCol();
    
    return (abs(nEndRow - nRow) + abs(nEndCol - nCol)) * 10;
}

HelloWorld::NODE* HelloWorld::inTable(int nIndex, vector<HelloWorld::NODE *> &vecTbl) {
    for (int i = 0; i < (int)vecTbl.size(); ++i) {
        if (nIndex == vecTbl[i]->nIndex) {
            return vecTbl[i];
        }
    }
    return NULL;
}

bool HelloWorld::onTouchBegan(cocos2d::Touch *pTouch, cocos2d::Event *pEvent) {
    if (!pTouch) {
        return false;
    }
    
    int nIndex = getIndexByPoint(pTouch->getLocation());
    if (m_vecGrid[nIndex]->gf == GRID_FLAG_OBSTACLE) {
        return false;
    }
    if (STEP_STARTPOINT == m_nStep) {
        m_nStartIndex = nIndex;
    }
    else if (STEP_ENDPOINT == m_nStep) {
        m_nEndIndex = nIndex;
    }
    else if (STEP_DEFAULT == m_nStep) {
        m_vecGrid[nIndex]->gf = GRID_FLAG_OBSTACLE;
    }
    
    return true;
}









































